assert(PandaDKPMaster, "PandaDKPMaster not found!")

------------------------------
--      Are you local?      --
------------------------------
local PandaDKPMaster = PandaDKPMaster
local Messages =
{
   Queue = {},
   Unauthorized = {},
   Failed = {},
}
PandaDKPMaster.Messages = Messages


function Messages:Enable()
end

function Messages:StartTimer()
   if (nil == Messages.Timer) then
      Messages.Timer = PandaDKPMaster:ScheduleRepeatingTimer(function() PandaDKPMaster.Messages:ProcessQueue() end, 1)
   end
end

function Messages:StopTimer()
   if (nil ~= Messages.Timer) then PandaDKPMaster:CancelTimer(Messages.Timer) end
   Messages.Timer = nil
end


function Messages:QueueMessage(sender, prefix, message)

   -- Create the message record
   local record = {}
   record.sender = sender
   record.prefix = prefix
   record.message = message
   
   -- If the message is from PDKP, then just queue it
   if (prefix == "PDKP") then
      tinsert(Messages.Queue, record)
      
   elseif (prefix == "PDKPM") then
   
      -- Skip messages from ourself
      if (sender == UnitName("player")) then return end
      
      -- Check to see if the sender is authorized (or the message is an authorization message)
      local command = PandaDKPMaster:ParseCommand(message)
      if ("AUTH" == command or "REAUTH" == command) then
         -- Process immediately
         Messages:ProcessMessage(record)
      elseif (PandaDKPMaster.Auth:CheckAuthorization(sender)) then
         -- Add the message to the queue
         tinsert(Messages.Queue, record)
      else
         -- Add the message to the unauthorized queue
         PandaDKPMaster:Print(string.format("Unauthorized message from %s: %s", sender, message))
         tinsert(Messages.Unauthorized, record)
      end
      
   end
end


-- Moves any messages for the specified sender from the unauthorized queue to the normal queue
function Messages:AuthorizeSender(sender)
   -- Loop through the records in the unauthorized list, moving any from that sender to the normal queue
   local newTable = {}
   for i,record in pairs(Messages.Unauthorized, record) do
      if (record.sender == sender) then
         tinsert(Messages.Queue, record)
      else
         tinsert(newTable, record)
      end
   end
   Messages.Unauthorized = newTable
end

-- Moves any messages for the specified sender from the normal queue to the unauthorized queue
function Messages:DeauthorizeSender(sender)
   -- Loop through the records in the normal list, moving any from that sender to the Unauthorized queue
   local newTable = {}
   for i,record in pairs(Messages.Queue, record) do
      if (record.sender == sender) then
         tinsert(Messages.Unauthorized, record)
      else
         tinsert(newTable, record)
      end
   end
   Messages.Queue = newTable
end


function Messages:ProcessQueue()
   -- If we are in combat exit immediately
   --if (UnitAffectingCombat("player")) then return end
   
   -- Pop up to five messages off the queue and process them
   for i = 1, 5 do
      -- See if the queue is empty
      if (0 == #Messages.Queue) then break end
      
      -- Pop the first message and process it
      Messages:ProcessMessage(tremove(Messages.Queue, 1))
   end
end


function Messages:ProcessMessage(record)
   -- Strip the command from the message
   local command, rest = PandaDKPMaster:ParseCommand(record.message)
   
   -- Switch the command so that we can parse the parameters
   if ("PDKPM" == record.prefix) then
      if ("AUTH" == command) then
         PandaDKPMaster.Auth:HandleAuth(record.sender, rest)
      elseif ("REAUTH" == command) then
         PandaDKPMaster.Auth:HandleReauth()
      elseif ("SYNC" == command) then
         Messages:HandleSyncCommand(record.sender, rest)
      elseif ("SYNCOK" == command) then
         Messages:HandleSyncOkCommand(record.sender, rest)
      elseif ("DIFF" == command) then
         Messages:HandleDiffCommand(record.sender, rest)
      elseif ("CHK" == command) then
         Messages:HandleCheckCommand(record.sender, rest)
      elseif ("UNK" == command) then
         Messages:HandleUnknownEventCommand(record.sender, rest)
      elseif ("PUB" == command) then
         PandaDKPMaster.Sync:HandlePubCommand(record.sender, rest)
      elseif ("PUBDONE" == command) then
         PandaDKPMaster.Sync:HandlePubDoneCommand(record.sender, rest)
      elseif ("ITEM" == command) then
         Messages:HandleItemCommand(record.sender, rest)
      elseif ("ALIAS" == command) then
         Messages:HandleAliasCommand(record.sender, rest)
      elseif ("DKP" == command) then
         Messages:HandleDkpCommand(record.sender, rest)
      elseif ("SI" == command) then
         Messages:HandleSetItemCommand(record.sender, rest)
      elseif ("AA" == command) then
      elseif ("AW" == command) then
         Messages:HandleAwardCommand(record.sender, rest)
      elseif ("PI" == command) then
         Messages:HandlePurchaseItemCommand(record.sender, rest)
      elseif ("TEST" == command) then
         PandaDKPMaster:Print(string.format("TEST command received: %s", rest))
      end
   elseif ("PDKP" == record.prefix) then
      if ("QUERY" == command) then
         PandaDKPMaster.Players:PublishDKPToGuild()
      elseif ("WHOSIT" == command) then
         if (not PandaDKPMaster.db.realm.DoNotDisturb) then
            PandaDKPMaster.Comm:SendClient("GUILD", "IT")
         end
      elseif ("BID" == command) then
         Messages:HandleBidCommand(record.sender, rest)
      elseif ("UNBID" == command) then
         Messages:HandleUnbidCommand(record.sender, rest)
      elseif ("AUDIT" == command) then
      else
         -- all the others we can safely ignore
      end
   end
end

      -- Timestamps are strings? in the format of yyyymmddhhmmss
      -- If DB is older then 21 days, throw away and start fresh
      
      -- AUTH md5
      -- REAUTH
      -- SYNC (DB | ITEM | ALIAS | DKP | EVENT) (ALL | date) checksum      Tell everyone what your checksum is for the whole DB or a particular table for all days or on a particular day.
      -- SYNCOK (DB | ITEM | ALIAS | DKP | EVENT) (ALL | date)             Your sync value matches mine
      -- DIFF (DB | ITEM | ALIAS | DKP | EVENT) (ALL | date) checksum      Opps, my value is different then yours
      -- CHK date {check1, ... checkN}                                     Here are my values. Response is UNK or (SI, AA, RA, AW, PI)
      -- UNK checksum                                                      I don't know about this message, please tell me
      
      -- These messages are used when someone wishes to rebuild their database
      -- PUB table                                                whisper requesting the table
      -- PUBDONE table                                            I'm done sending table data
      -- ITEM itemId dkp "pool" "type" "subtype" "name"           reply for items
      -- ALIAS player alias                                       reply for aliases
      -- DKP player "pool" dkp                                    reply for DKP (actually the 30 day summary)
      
      -- These messages are the only ones that are synced
      -- SI timestamp itemId dkp "pool" "type" "subtype" "name"   Set Item
      -- AA timestamp player alias                                Add Alias
      -- RA timestamp player alias                                Remove Alias
      -- AW timestamp "pool" dkp "notes" {player1, ... playerN}   Award DKP
      -- PI timestamp "pool" dkp itemId player loot# (N/G)        Purchase Item  loot# is position in list to make identical items unique (tier tokens)
      
      -- On sync, first see if we need to summarize the DB.
   
      -- AWARDED dkp "pool" {player1, ... playerN}
      -- PURCHASED player dkp "pool" link
      -- QUERY "<player | class | ALL>"
      -- DKP player "class" {"pool" = dkp ... }
      -- IT
      -- NOTIT
      -- WHOSIT
      -- OPEN dkp "pool" link
      -- CLOSED
      -- CANCEL
      -- BID itemId (N/G)
      -- ACCEPTED itemId (N/G/U)
      -- UNBID itemId
      -- AUDIT player "pool"                             Get a 30 day list of events for that player and pool
      -- AUDITB timestamp player "pool" dkp
      -- AUDITA timestamp player "pool" dkp "notes"
      -- AUDITP timestamp player "pool" dkp itemid N/G
      
      
      

function Messages:HandleSyncCommand(sender, rest)
   local _, _, type, datestamp, checksum = strfind(rest, '(.+) (.+) (.+)')
   if (nil == type or nil == datestamp or nil == checksum) then return end
   PandaDKPMaster.Sync:ReceivedSync(sender, type, datestamp, checksum)
end

function Messages:HandleSyncOkCommand(sender, rest)
   local _, _, type, datestamp = strfind(rest, '(.+) (.+)')
   if (nil == type or nil == datestamp) then return end
   PandaDKPMaster.Sync:ReceivedSyncOk(sender, type, datestamp)
end

function Messages:HandleDiffCommand(sender, rest)
   local _, _, type, datestamp, checksum = strfind(rest, '(.+) (.+) (.+)')
   if (nil == type or nil == datestamp or nil == checksum) then return end
   PandaDKPMaster.Sync:ReceivedDiff(sender, type, datestamp, checksum)
end

function Messages:HandleCheckCommand(sender, rest)
   local _, _, datestamp, checkList = strfind(rest, '(.+) {(.*)}')
   if (nil == datestamp or nil == checkList) then return end
   local checksums = {}
   for checksum in string.gmatch(checkList, '(%w%w%w%w%w%w%w%w%w%w%w%w%w%w%w%w%w%w%w%w%w%w%w%w%w%w%w%w%w%w%w%w)') do
      tinsert(checksums, checksum)
   end
   PandaDKPMaster.Sync:ReceivedChecklist(sender, datestamp, checksums)
end

function Messages:HandleUnknownEventCommand(sender, rest)
   local _, _, checksum = strfind(rest, '(%w%w%w%w%w%w%w%w%w%w%w%w%w%w%w%w%w%w%w%w%w%w%w%w%w%w%w%w%w%w%w%w)')
   if (nil == checksum) then return end
   PandaDKPMaster.Events:SendEvent(checksum)
end

function Messages:HandleSetItemCommand(sender, rest)
   local _, _, timestamp, itemId, dkp, pool, itemtype, subtype, name =
    strfind(rest, '(.+) (.+) (.+) "(.+)" "(.+)" "(.+)" "(.+)"')
   if (nil == timestamp or nil == itemId or nil == dkp or nil == pool or nil == itemtype or nil == subtype or nil == name) then return end
   
   PandaDKPMaster.Items:OnSetItemMessage(tostring(timestamp), tostring(itemId), tonumber(dkp),
    tostring(pool), tostring(itemtype), tostring(subtype), tostring(name))
end

function Messages:HandleAwardCommand(sender, rest)
   local _, _, timestamp, pool, dkp, notes, playerlist =
    strfind(rest, '(.+) "(.+)" (.+) "(.+)" {(.+)}')
   if (nil == timestamp or nil == pool or nil == dkp or nil == notes or nil == playerlist) then return end
   
   -- Parse the player list
   local players = {}
   for player in string.gmatch(playerlist, '([^%s,]+)') do
      tinsert(players, player)
   end
   
   PandaDKPMaster.Awarding:OnAwardMessage(tostring(timestamp), tostring(pool), tonumber(dkp),
    tostring(notes), players)
end

function Messages:HandlePurchaseItemCommand(sender, rest)
   local _, _, timestamp, pool, dkp, itemId, player, loot, needOrGreed =
    strfind(rest, '(.+) "(.+)" (.+) (.+) (.+) (.+) (.+)')
   if (nil == timestamp or nil == pool or nil == dkp or nil == itemId or nil == player or nil == loot or nil == needOrGreed) then return end
   
   PandaDKPMaster.Players:OnPurchaseItemMessage(tostring(timestamp), tostring(player), tostring(itemId),
    tonumber(dkp), tostring(pool), tonumber(loot), tostring(needOrGreed))
end

function Messages:HandleItemCommand(sender, rest)
   local _, _, itemId, dkp, pool, itemtype, subtype, name = 
    strfind(rest, '(.+) (.+) "(.+)" "(.+)" "(.+)" "(.+)"')
   if (nil == itemId or nil == dkp or nil == pool or nil == itemtype or nil == subtype or nil == name) then return end
   
   PandaDKPMaster.Sync:OnItemMessage(tostring(itemId), tonumber(dkp), tostring(pool), tostring(itemtype),
    tostring(subtype), tostring(name))
end

function Messages:HandleAliasCommand(sender, rest)
   local _, _, player, alias = strfind(rest, '(.+) (.+)')
   if (nil == player or nil == alias) then return end
   
   PandaDKPMaster.Sync:OnAliasMessage(tostring(player), tostring(alias))
end

function Messages:HandleDkpCommand(sender, rest)
   local _, _, player, pool, dkp = strfind(rest, '(.+) "(.+)" (.+)')
   if (nil == player or nil == pool or nil == dkp) then return end
   
   PandaDKPMaster.Sync:OnDkpMessage(tostring(player), tostring(pool), tonumber(dkp))
end


      

function Messages:HandleBidCommand(sender, rest)
   local _, _, itemId, state = strfind(rest, '(%d+) (%a)')
   if (nil == itemId or nil == state) then return end
   PandaDKPMaster.Looting:BidReceived(sender, itemId, state)
end

function Messages:HandleUnbidCommand(sender, rest)
   local _, _, itemId = strfind(rest, '(.+)')
   if (nil == itemId) then return end
   PandaDKPMaster.Looting:BidReceived(sender, itemId, 'U')
end





function Messages:CreateSIMessage(timestamp, itemId, dkp, pool, type, subtype, name)
   return string.format('SI %s %s %.2f "%s" "%s" "%s" "%s"', timestamp, itemId, dkp, pool, type, subtype, name)
end

function Messages:CreatePIMessage(timestamp, pool, dkp, itemId, player, loot, needOrGreed)
   return string.format('PI %s "%s" %.2f %s %s %d %s', timestamp, pool, dkp, itemId, player, loot, needOrGreed)
end

function Messages:CreateAWMessage(timestamp, pool, dkp, notes, players)
   return string.format('AW %s "%s" %.2f "%s" {%s}', timestamp, pool, dkp, notes, table.concat(players, ', '))
end

function Messages:CreateAwardMessage(timestamp, pool, dkp, notes, players)
   return string.format('AWARDED %.2f "%s" {%s}', dkp, pool, table.concat(players, ', '))
end

function Messages:CreatePurchaseMessage(pool, dkp, link, player)
   return string.format('PURCHASED %s %.2f "%s" %s', player, dkp, pool, link)
end